package com.xiaohei.crm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaohei.crm.common.exception.XiaoheiException;
import com.xiaohei.crm.common.result.Result;
import com.xiaohei.crm.common.util.AESUtil;
import com.xiaohei.crm.common.util.JwtHelper;
import com.xiaohei.crm.dao.User;
import com.xiaohei.crm.dao.dto.UserLoginReqDTO;
import com.xiaohei.crm.dao.dto.UserLoginRespDTO;
import com.xiaohei.crm.mapper.UserMapper;
import com.xiaohei.crm.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.xiaohei.crm.common.RedisEnums.USER_LOGIN_KEY;
import static com.xiaohei.crm.common.RedisEnums.USER_REGISTRY_KEY;
import static com.xiaohei.crm.common.result.ResultCodeEnum.*;

/**
 * ClassName: UserServiceImpl
 * Package:
 * Description:
 *
 * @Author:xiaohei
 * @Create 2024/6/19 16:19
 * Version 1.0
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private final UserMapper userMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final RBloomFilter userRegisterCachePenetrationBloomFilter;

    /**
     * 根据名字查询数据库
     *
     * @param username
     * @return
     */
    @Override
    public User getByUsername(String username) {
        String s = stringRedisTemplate.opsForValue().get(USER_REGISTRY_KEY + username);
        if(s == null){
            throw new XiaoheiException(USER_IS_NULL);
        }

        LambdaQueryWrapper<User> eq = Wrappers.lambdaQuery(User.class).eq(User::getUsername, username);
        User user = baseMapper.selectOne(eq);
        if(ObjectUtil.isNotEmpty(user)){
            stringRedisTemplate.opsForValue()
                    .set(USER_REGISTRY_KEY + user.getUsername(),user.getUsername(),30L, TimeUnit.MINUTES);

        }
        return user;
    }

    @Override
    public Result<IPage> findByPage() {
        //设置每页起始值及每页条数
        Page<User> page = new Page<>();
        LambdaQueryWrapper<User> eq = Wrappers.lambdaQuery(User.class).eq(User::getDelFlag, 0);
        return Result.ok(userMapper.selectPage(page, eq));
    }

    /**
     * 添加用户 or 更新用户信息
     *
     * @param user 请求参数  有用户id则走更新   没有即为添加
     * @return
     */
    @Override
    public void saveUser(User user) {
        if (userRegisterCachePenetrationBloomFilter.contains(user.getUsername())) {
            throw new XiaoheiException(DATA_ERROR);
        }
        if(user == null){
            throw new XiaoheiException(USER_IS_NULL);
        }

        String s = stringRedisTemplate.opsForValue().get(USER_REGISTRY_KEY + user.getUsername());
        if(s != null){
            throw new XiaoheiException(USER_ALREADY_EXISTS);
        }

        String paramsUsername = user.getUsername();
        LambdaQueryWrapper<User> eq = Wrappers.lambdaQuery(User.class)
                .eq(User::getUsername, paramsUsername)
                .eq(User::getDelFlag,0);
        User databaseUser = baseMapper.selectOne(eq);
        if(databaseUser != null){
            throw new XiaoheiException(400,"用户名重复");
        }

        String encrypt = AESUtil.encrypt(user.getPassword());
        user.setPassword(encrypt);
        int isInsert = baseMapper.insert(user);
        if (isInsert == 0) {
            throw new XiaoheiException(DATA_ERROR);
        }

        userRegisterCachePenetrationBloomFilter.add(user.getUsername());
        stringRedisTemplate.opsForValue()
                .set(USER_REGISTRY_KEY + user.getUsername(),user.getUsername(),30L, TimeUnit.MINUTES);
    }

    /**
     * 根据用户名称修改用户信息
     *
     * @param user
     */
    @Override
    public void updateByUsername(User user) {
        if (!userRegisterCachePenetrationBloomFilter.contains(user.getUsername())) {
            throw new XiaoheiException(DATA_ERROR);
        }

        String s = stringRedisTemplate.opsForValue().get(USER_REGISTRY_KEY + user.getUsername());
        if(s == null){
            throw new XiaoheiException(USER_IS_NULL);
        }

        LambdaQueryWrapper<User> eq = Wrappers.lambdaQuery(User.class).eq(User::getUsername, user.getUsername());
        int update = baseMapper.update(user, eq);

        if(update < 1){
            throw new XiaoheiException(CLIENT_EXCEPTION);
        }

        stringRedisTemplate.expire(USER_REGISTRY_KEY + user.getUsername(),30L, TimeUnit.MINUTES);
    }

    @Override
    public void removeByUsername(String username) {
        if (!userRegisterCachePenetrationBloomFilter.contains(username)) {
            throw new XiaoheiException(DATA_ERROR);
        }
        String s = stringRedisTemplate.opsForValue().get(USER_REGISTRY_KEY + username);
        if(s == null){
            throw new XiaoheiException(USER_IS_NULL);
        }
        LambdaUpdateWrapper<User> updateWrapper = Wrappers.lambdaUpdate(User.class).eq(User::getUsername, username);
        User user = User.builder().delFlag(1).build();
        int update = baseMapper.update(user, updateWrapper);

        if(update < 1){
            throw new XiaoheiException(CLIENT_EXCEPTION);
        }

        stringRedisTemplate.delete(USER_REGISTRY_KEY + username);
    }

    @Override
    public UserLoginRespDTO login(UserLoginReqDTO requestParams) {
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(USER_LOGIN_KEY + requestParams.getUsername());
        if(CollUtil.isNotEmpty(entries)){
            Object firstKey = null;
            for (Map.Entry<Object, Object> entry : entries.entrySet()) {
                firstKey = entry.getKey();
                Object firstValue = entry.getValue();
                // 只获取第一个条目，所以获取后即可退出循环
                break;
            }
            return new UserLoginRespDTO((String) firstKey);
        }

        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class)
                .eq(User::getUsername, requestParams.getUsername())
                .eq(User::getPassword, AESUtil.encrypt(requestParams.getPassword()))
                .eq(User::getDelFlag,0);

        User user = baseMapper.selectOne(wrapper);
        if(user == null){
            throw new XiaoheiException(USER_IS_NOT_EXIST);
        }

        Map<Object,Object> hasLoginMap = stringRedisTemplate.opsForHash().entries(USER_LOGIN_KEY + requestParams.getUsername());
        if(CollUtil.isNotEmpty(hasLoginMap)){
            stringRedisTemplate.expire(USER_LOGIN_KEY + requestParams.getUsername(), 30L, TimeUnit.MINUTES);
            String token = hasLoginMap.keySet()
                    .stream()
                    .findFirst()
                    .map(Object::toString)
                    .orElseThrow(() -> new XiaoheiException(DATA_ERROR));
            return new UserLoginRespDTO(token);
        }

        String uuid = UUID.randomUUID().toString();
        stringRedisTemplate.opsForHash().put(USER_LOGIN_KEY + requestParams.getUsername(),uuid, JSON.toJSONString(user));
        stringRedisTemplate.expire(USER_LOGIN_KEY + requestParams.getUsername(),30L, TimeUnit.MINUTES);
        return new UserLoginRespDTO(uuid);
    }

    @Override
    public Boolean checkLogin(String username, String token) {
        return  stringRedisTemplate.opsForHash().get(USER_LOGIN_KEY + username, token) != null;
    }

    @Override
    public void logOut(String username, String token) {
        if(checkLogin(username, token)){
            stringRedisTemplate.delete(USER_LOGIN_KEY  + username);
            return;
        }
        throw new XiaoheiException(DATA_ERROR);
    }

    @Override
    public UserLoginRespDTO loginWithJwt(HttpServletRequest request, HttpServletResponse response, UserLoginReqDTO requestParams) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class)
                .eq(User::getUsername, requestParams.getUsername())
                .eq(User::getPassword, requestParams.getPassword())
                .eq(User::getDelFlag,0);

        User user = baseMapper.selectOne(wrapper);
        if(user == null){
            throw new XiaoheiException(USER_IS_NOT_EXIST);
        }

        String token = JwtHelper.createToken(requestParams.getUsername());
        response.addHeader("X-token",token);
        return new UserLoginRespDTO(token);
    }

}
